/**
 * ReportGear(2011)
 */
package net.abbp.report.model;

import java.awt.Rectangle;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import net.abbp.core.util.NumberUtils;
import net.abbp.core.util.StringUtils;
import net.abbp.report.view.GridView;

import com.reportgear.core.api.BaseObject;
import com.reportgear.report.model.auxiliary.ColumnPropList;
import com.reportgear.report.model.auxiliary.ColumnProperty;
import com.reportgear.report.model.auxiliary.ColumnRow;
import com.reportgear.report.model.auxiliary.ColumnType;
import com.reportgear.report.model.auxiliary.RowPropList;
import com.reportgear.report.model.auxiliary.RowProperty;
import com.reportgear.report.model.auxiliary.RowType;
import com.reportgear.report.parameter.model.Parameter;
import com.reportgear.report.setting.ReportSettings;
import com.reportgear.report.view.grid.selection.Selection;

/**
 * 报表模型
 * 
 * @version 1.0 2011-3-11
 * @author <a herf="lexloo@gmail.com">lexloo</a>
 * @since Report 1.0
 */
public class ReportModel extends BaseObject {
    /**
     * 默认空对象
     */
    public static final ReportModel EMPTY_MODEL = new ReportModel();
    /**
     * 模型版本
     */
    private String version = "1.0";
    // 模型的基本信息，针对不同类型的模型，包含一个不同类型的信息类
    private InfoObject infoObject;
    // 列属性列表
    private ColumnPropList columnPropList = new ColumnPropList();
    // 行属性列表
    private RowPropList rowPropList = new RowPropList();;
    // 报表配置
    private ReportSettings reportSettings = new ReportSettings();;
    // 模型参数
    private List<Parameter> parameters = new ArrayList<Parameter>();;
    // 单元格容器
    private CellGrid cellGrid = new CellGrid();

    // 属性改变事件支持
    private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
    private transient Object objectLock = new Object();

    /**
     * 构造函数
     */
    public ReportModel() {
    }

    private Object getObjectLock() {
        return objectLock;
    }

    /**
     * 添加属性改变监听器
     * 
     * @param listener 监听器
     */
    public void addPropertyChangeListener(PropertyChangeListener listener) {
        synchronized (getObjectLock()) {
            if (listener == null) {
                return;
            }
            if (changeSupport == null) {
                changeSupport = new PropertyChangeSupport(this);
            }
            changeSupport.addPropertyChangeListener(listener);
        }
    }

    /**
     * 移除属性改变监听器
     * 
     * @param listener 监听器
     */
    public void removePropertyChangeListener(PropertyChangeListener listener) {
        synchronized (getObjectLock()) {
            if (listener == null || changeSupport == null) {
                return;
            }
            changeSupport.removePropertyChangeListener(listener);
        }
    }

    /**
     * 添加属性改变监听器
     * 
     * @param propertyName 属性名
     * @param listener 监听器
     */
    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        synchronized (getObjectLock()) {
            if (listener == null) {
                return;
            }
            if (changeSupport == null) {
                changeSupport = new PropertyChangeSupport(this);
            }
            changeSupport.addPropertyChangeListener(propertyName, listener);
        }
    }

    /**
     * 移除属性改变监听器
     * 
     * @param propertyName 属性名
     * @param listener 监听器
     */
    public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        synchronized (getObjectLock()) {
            if (listener == null || changeSupport == null) {
                return;
            }
            changeSupport.removePropertyChangeListener(propertyName, listener);
        }
    }

    /**
     * 触发属性改变
     * 
     * @param propertyName 属性值
     * @param oldValue 旧值
     * @param newValue 新值
     */
    public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
        PropertyChangeSupport changeSupport;
        synchronized (getObjectLock()) {
            changeSupport = this.changeSupport;
        }
        if (changeSupport == null || (oldValue != null && newValue != null && oldValue.equals(newValue))) {
            return;
        }
        changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }

    /**
     * 获取列属性列表
     * 
     * @return 列属性列表
     */
    public ColumnPropList getColumnPropList() {
        return this.columnPropList;
    }

    /**
     * 获取行属性列表
     * 
     * @return 行属性列表
     */
    public RowPropList getRowPropList() {
        return this.rowPropList;
    }

    /**
     * 获取Cell像素范围
     * 
     * @param worksheet
     *            工作薄
     * @param cell
     *            单元格
     * @return Cell像素范围
     */
    public Rectangle getCellRectangle(GridView worksheet, Cell cell) {
        return this.getCellRectangle(worksheet, cell.getColumn(), cell.getRow(), cell.getColumnSpan(), cell
                .getRowSpan());
    }

    /**
     * 获取Cell像素范围
     * 
     * @param worksheet
     *            工作薄
     * @param selection
     *            选择集
     * @return Cell像素范围
     */
    public Rectangle getCellRectangle(GridView worksheet, Selection selection) {
        return this.getCellRectangle(worksheet, selection.getColumn(), selection.getRow(), selection.getColumSpan(),
                selection.getRowSpan());
    }

    /**
     * 获取Cell像素范围
     * 
     * @param column
     *            Cell所在列
     * @param row
     *            Cell所在行
     * @param columnSpan
     *            Cell列跨度
     * @param rowSpan
     *            Cell行跨度
     * @return Cell像素范围
     */
    public Rectangle getCellRectangle(GridView worksheet, int column, int row, int columnSpan, int rowSpan) {
        int x = this.columnPropList.getRangeValue(worksheet.getHoriBeginValue(), column);
        int y = this.rowPropList.getRangeValue(worksheet.getVertBeginValue(), row);

        int w = 0;
        for (int i = 0; i < columnSpan; i++) {
            w += this.columnPropList.getWidth(column + i);
        }

        int h = 0;
        for (int j = 0; j < rowSpan; j++) {
            h += this.rowPropList.getHeight(row + j);
        }

        return new Rectangle(x, y, w, h);
    }

    /**
     * 添加单元格
     * 
     * @param cell 单元格
     */
    public void addCell(Cell cell) {
        int oldColumnCount = this.getColumnCount();
        int oldRowCount = this.getRowCount();
        this.cellGrid.add(cell);

        this.firePropertyChange("ColumnCount", oldColumnCount, this.getColumnCount());
        this.firePropertyChange("RowCount", oldRowCount, this.getRowCount());
    }

    /**
     * 移除单元格
     * 
     * @param cell 单元格
     */
    public void removeCell(Cell cell) {
        this.cellGrid.remove(cell);
    }

    /**
     * 添加列
     * 
     * @param index
     *            列索引
     */
    public void addColumn(int index) {
        int oldColumnCount = this.getColumnCount();

        this.checkMergedCells(this.getColumn(index));
        //
        this.columnPropList.add(index, new ColumnProperty());
        this.cellGrid.addColumn(index);

        this.firePropertyChange("ColumnCount", oldColumnCount, this.getColumnCount());
    }

    /**
     * 添加行
     * 
     * @param index
     *            行索引
     */
    public void addRow(int index) {
        int oldRowCount = this.getRowCount();
        this.checkMergedCells(this.getRow(index));

        this.rowPropList.add(index, new RowProperty());
        this.cellGrid.addRow(index);

        this.firePropertyChange("RowCount", oldRowCount, this.getRowCount());
    }

    /**
     * 移除列
     * 
     * @param index
     *            列索引(0 基)
     */
    public void removeColumn(int index) {
        this.checkMergedCells(this.getColumn(index));

        this.columnPropList.remove(index);
        this.cellGrid.deleteColumn(index);

    }

    /**
     * 移除行
     * 
     * @param index
     *            行索引(0 基)
     */
    public void removeRow(int index) {
        this.checkMergedCells(this.getRow(index));

        this.rowPropList.remove(index);
        this.cellGrid.deleteRow(index);
    }

    /**
     * 检查合并单元格，如果单元格被合并，则拆分合并主格
     * 
     * @param cells
     *            检查单元格集
     */
    private void checkMergedCells(Iterator<Cell> cells) {
        while (cells.hasNext()) {
            Cell cell = cells.next();
            if (cell.isMerged()) {
                this.unmergeCell(cell);
            }
        }
    }

    /**
     * 获取指定单元格
     * 
     * @param column
     *            行
     * @param row
     *            列
     * @return 单元格
     */
    public Cell getCell(int column, int row) {
        return this.cellGrid.getCell(column, row);
    }

    /**
     * 获取指定单元格
     * 
     * @param sel
     *            区域
     * @return 单元格
     */
    public Cell getCell(Selection sel) {
        return this.getCell(sel.getColumn(), sel.getRow());
    }

    /**
     * 获取列单元格迭代器
     * 
     * @param index
     *            列索引
     * @return 列单元格迭代器
     */
    public Iterator<Cell> getColumn(int index) {
        return this.cellGrid.getColumn(index);
    }

    /**
     * 获取所有单元格迭代器
     * 
     * @return 单元格迭代器
     */
    public Iterator<Cell> getCellIterator() {
        return this.cellGrid.cellIterator();
    }

    /**
     * 获取包含指定对象类的Cell迭代器
     * 
     * @param clazz
     *            指定对象类
     * @return 迭代器
     */
    public Iterator<Cell> getCellsIteratorByCellElementClass(Class<?> clazz) {
        return this.cellGrid.getCellsByElementClass(clazz);
    }

    /**
     * 获取行单元格索引
     * 
     * @param index
     *            行索引
     * @return 行单元格索引
     */
    public Iterator<Cell> getRow(int index) {
        return this.cellGrid.getRow(index);
    }

    /**
     * 
     * Cell交叉集
     * 
     * @param columnFrom
     *            开始行 0 based
     * @param rowFrom
     *            开始列 0 based
     * @param columnSpan
     *            列跨度
     * @param rowSpan
     *            行跨度
     * @return 迭代器
     */
    public Iterator<Cell> intersect(int columnFrom, int rowFrom, int columnTo, int rowTo) {
        return this.cellGrid.intersect(columnFrom, rowFrom, columnTo, rowTo);
    }

    /**
     * 获取列宽度
     * 
     * @param index
     *            列索引
     * @return 列宽度
     */
    public int getColumnWidth(int index) {
        return this.columnPropList.getWidth(index);
    }

    /**
     * 设置列宽度
     * 
     * @param index
     *            列索引
     * @param width
     *            列宽度
     */
    public void setColumnWidth(int index, int width) {
        this.columnPropList.setWidth(index, width);
    }

    /**
     * 获取列类型
     * 
     * @param index
     *            列索引
     * @return 列类型
     */
    public ColumnType getColumnType(int index) {
        return this.columnPropList.getType(index);
    }

    /**
     * 设置列类型
     * 
     * @param index
     *            列索引
     * @param rowType
     *            列类型
     */
    public void setColumnType(int index, ColumnType columnType) {
        this.columnPropList.setType(index, columnType);
    }

    /**
     * 获取行高度
     * 
     * @param index
     *            行索引
     * @return 行高度
     */
    public int getRowHeight(int index) {
        return this.rowPropList.getHeight(index);
    }

    /**
     * 设置行高度
     * 
     * @param index
     *            行索引
     * @param height
     *            行高度
     */
    public void setRowHeight(int index, int height) {
        this.rowPropList.setHeight(index, height);
    }

    /**
     * 获取行类型
     * 
     * @param index
     *            行索引
     * @return 行类型
     */
    public RowType getRowType(int index) {
        return this.rowPropList.getType(index);
    }

    /**
     * 设置行类型
     * 
     * @param index
     *            索引
     * @param rowType
     *            行类型
     */
    public void setRowType(int index, RowType rowType) {
        this.rowPropList.setType(index, rowType);
    }

    /**
     * 合并单元格
     * 
     * @param column
     *            列
     * 
     * @param row
     *            行
     * @param columnSpan
     *            列跨度
     * @param rowSpan
     *            行跨度
     */
    public void mergeCell(int column, int row, int columnSpan, int rowSpan) {
        Cell cell = this.cellGrid.getCell(column, row);

        List<Cell> forRemove = new ArrayList<Cell>();
        Iterator<Cell> itr = this.cellGrid.intersect(column, row, columnSpan, rowSpan);
        while (itr.hasNext()) {
            Cell c = itr.next();
            if (c != cell) {
                forRemove.add(c);
            }
        }

        // 必须要一起删除，应为迭代器中使用了cellGrid的size
        for (Cell c : forRemove) {
            this.cellGrid.remove(c);
        }

        if (cell == Cell.EMPTY_CELL) {
            cell = new Cell(column, row);
        }

        cell.setColumnSpan(columnSpan);
        cell.setRowSpan(rowSpan);
        this.addCell(cell);
    }

    /**
     * 取消单元格合并
     * 
     * @param column
     *            列
     * @param row
     *            行
     * @param columnSpan
     *            列跨度
     * @param rowSpan
     *            行跨度
     */
    public void unmergeCell(int column, int row, int columnSpan, int rowSpan) {
        Iterator<Cell> iterator = this.cellGrid.intersect(column, row, columnSpan, rowSpan);

        while (iterator.hasNext()) {
            Cell c = iterator.next();
            this.unmergeCell(c);
        }
    }

    /**
     * 取消单元格合并
     * 
     * @param cell
     *            单元格
     */
    private void unmergeCell(Cell cell) {
        cell.setColumnSpan(1);
        cell.setRowSpan(1);
    }

    /**
     * 设置单元格的值
     * 
     * @param column
     *            列
     * @param row
     *            行
     * @param value
     *            值
     */
    public void setCellValue(int column, int row, Object value) {
        this.cellGrid.setCellValue(column, row, value);
    }

    /**
     * 增加对象到指定的矩形框中的所有单元格
     * 
     * @param value
     *            要加入的单元格对象
     * @param sel
     *            选择范围
     */
    public void setCellValue(Selection sel, Object value) {
        int column = sel.getColumn();
        int row = sel.getRow();

        for (int i = 0, lenX = sel.getColumSpan(); i < lenX; i++) {
            for (int j = 0, lenY = sel.getRowSpan(); j < lenY; j++) {
                this.setCellValue(column, row, value);
            }
        }
    }

    /**
     * 获取合并到的单元格
     * 
     * @param column 列
     * @param row 行
     * @return 单元格
     */
    public Cell getMergedToCell(int column, int row) {
        for (Cell c : this.cellGrid.cells) {
            if (c.isInCell(column, row)) {
                return c;
            }
        }

        return Cell.EMPTY_CELL;
    }

    /**
     * 获取单元格的浮点值
     * 
     * @param column
     *            行
     * @param row
     *            列
     * @return 单元格浮点值
     */
    public double getCellDoubleValue(int column, int row) {
        Cell cell = this.getCell(column, row);

        if (cell != null) {
            Object element = cell.getValue();

            if (element != null) {
                return NumberUtils.convertToDouble(element, 0.0D);
            }
        }

        return 0.0D;
    }

    /**
     * 获取单元格的值
     * 
     * @param cr
     *            ColumnRow对象
     * @return 单元格浮点值
     */
    public double getCellDoubleValue(ColumnRow cr) {
        return this.getCellDoubleValue(cr.getColumn(), cr.getRow());
    }

    /**
     * @return 列总数
     */
    public int getColumnCount() {
        return this.cellGrid.getColumnCount();
    }

    /**
     * @return 行总数
     */
    public int getRowCount() {
        return this.cellGrid.getRowCount();
    }

    /**
     * 报表设置
     * 
     * @param reportSettings
     *            报表设置
     */
    public void setReportSettings(ReportSettings reportSettings) {
        this.reportSettings = reportSettings;
    }

    /**
     * 获取报表设置
     * 
     * @return 报表设置
     */
    public ReportSettings getReportSettings() {
        return reportSettings;
    }

    /**
     * 添加参数
     * 
     * @param parameter
     *            参数对象
     */
    public void addParameter(Parameter parameter) {
        this.parameters.add(parameter);
    }

    /**
     * 获取模型参数列表
     * 
     * @return 参数列表
     */
    public List<Parameter> getParameters() {
        return this.parameters;
    }

    /**
     * 根据参数名称获取参数值
     * 
     * @param name
     *            参数名称
     * @return 参数值
     */
    public Object getParameter(String name) {
        if (!StringUtils.isEmpty(name)) {
            for (Parameter para : this.parameters) {
                if (name.equals(para.getName())) {
                    return para.getObject();
                }
            }
        }

        return "";
    }

    /**
     * 
     * @return 获取固定列首
     */
    public List<Integer> getFixedHeaderColumns() {
        return getColumnsByType(ColumnType.HEADER);
    }

    /**
     * 
     * @return 获取固定列尾
     */
    public List<Integer> getFixedFooterColumns() {
        return getColumnsByType(ColumnType.FOOTER);
    }

    /**
     * 获取指定类型的列
     * 
     * @param type
     *            指定类型
     * @return 列索引列表
     */
    private List<Integer> getColumnsByType(ColumnType type) {
        List<Integer> result = new ArrayList<Integer>();

        for (int i = 0, size = this.getColumnCount(); i < size; i++) {
            ColumnProperty p = this.columnPropList.get(i);

            if (p.getType() == type) {
                result.add(i);
            }
        }

        return result;
    }

    /**
     * 是否固定列
     * 
     * @param column
     *            指定列
     * @return 是否固定列
     */
    public boolean isFixedColumn(int column) {
        ColumnProperty p = this.columnPropList.get(column);

        return p.getType() == ColumnType.HEADER || p.getType() == ColumnType.FOOTER;
    }

    /**
     * 
     * @return 获取固定行首
     */
    public List<Integer> getFixedHeaderRows() {
        return getFixedRowsByType(RowType.HEADER);
    }

    /**
     * 
     * @return 获取固定行尾部
     */
    public List<Integer> getFixedFooterRows() {
        return getFixedRowsByType(RowType.FOOTER);
    }

    /**
     * 获取指定类型的行
     * 
     * @param type
     *            指定类型
     * @return 行索引列表
     */
    private List<Integer> getFixedRowsByType(RowType type) {
        List<Integer> result = new ArrayList<Integer>();

        for (int i = 0, size = this.getRowCount(); i < size; i++) {
            RowProperty p = this.rowPropList.get(i);

            if (p.getType() == type) {
                result.add(i);
            }
        }

        return result;
    }

    /**
     * 是否固定行
     * 
     * @param row
     *            指定行
     * @return 是否固定行
     */
    public boolean isFixedRow(int row) {
        RowProperty p = this.rowPropList.get(row);

        return p.getType() == RowType.HEADER || p.getType() == RowType.FOOTER;
    }

    /**
     * 报表模型克隆
     * 
     * @return 报表模型
     */
    @Override
    public ReportModel clone() {
        ReportModel model = (ReportModel) super.clone();
        model.columnPropList = this.columnPropList.clone();
        model.rowPropList = this.rowPropList.clone();
        model.cellGrid = this.cellGrid.clone();

        return model;
    }

    /**
     * 复制行属性
     * 
     * @param dst
     *            目标行索引
     * @param src
     *            源行索引
     */
    public void copyRowProperties(int dst, int src) {
        // 复制高度
        this.setRowHeight(dst, this.getRowHeight(src));
        // 复制行属性
        this.setRowType(dst, this.getRowType(src));
        // 复制样式
        for (int i = 0, size = this.getColumnCount(); i < size; i++) {
            this.getCell(i, dst).setStyle(this.getCell(i, src).getStyle());
        }
    }

    /**
     * 获取报表模型的版本
     * 
     * @return 报表模型的版本
     */
    public String getVersion() {
        return version;
    }

    /**
     * 设置信息对象
     * 
     * @param infoObject 信息对象
     */
    public void setInfoObject(InfoObject infoObject) {
        this.infoObject = infoObject;
    }

    /**
     * 获取信息对象
     * 
     * @return 信息对象
     */
    public InfoObject getInfoObject() {
        return infoObject;
    }
}
